#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include "ringbuffer.h"

static void RingBuffer_CaculateTotalCount(RingBuffer *const self)
{
    buf_size_t total_count = 0;
    RingBufferSection *sect = self->head.sect;
    RingBufferSection *tail_node = self->head.sect;
    if (sect == NULL) {
        self->total_buf_len = 0;
        return;
    }

    do {
        total_count += sect->buflen;
        sect = sect->next;
    } while (sect != tail_node);

    self->total_buf_len = total_count;
}

static buf_size_t RingBuffer_GetTotalCount(RingBuffer *const self)
{
    return self->total_buf_len;
}

static inline bool check_need_copy_data(RingBuffer *const self)
{
    if (self->tail.sect != self->head.sect)
        return false;
    if (self->head.offset < self->tail.offset) {
        return true;
    }
    if (self->total_buf_len == self->used_buf_len) {
        return true;
    }
    return false;
}

static void RingBuffer_ExtendBuffer(RingBuffer *const self, RingBufferSection *buffer_sect)
{
    buffer_sect->next = NULL;
    if (self->buffer_list == NULL) {
        buffer_sect->next = buffer_sect;
        self->buffer_list = buffer_sect;
        self->head.sect = buffer_sect;
        self->tail.sect = buffer_sect;
    } else {
        RingBufferSection *sect = self->head.sect;
        RingBufferSection *next_node = sect->next;
        sect->next = buffer_sect;
        buffer_sect->next = next_node;
        if (check_need_copy_data(self)) {
            // TODO check len limit
            memcpy(buffer_sect->buf, sect->buf, self->head.offset);
            self->head.sect = self->head.sect->next;
        }
    }
    RingBuffer_CaculateTotalCount(self);
}

static buffer_err RingBuffer_ExtendDynamicBuffer(RingBuffer *const self, buf_size_t size)
{
    RingBufferSection *ptr = (RingBufferSection *)malloc(sizeof(RingBufferSection) + size);
    if (ptr == NULL) {
        return RING_BUFFER_FAIL;
    }
    ptr->buf = (uint8_t *)&ptr[1];
    ptr->buflen = size;
    RingBuffer_ExtendBuffer(self, ptr);
    return RING_BUFFER_OK;
}

static buffer_err RingBuffer_ExtendStaticBuffer(RingBuffer *const self, RingBufferSection *buffer_sect)
{
    if (buffer_sect == NULL) {
        return RING_BUFFER_FAIL;
    }
    RingBuffer_ExtendBuffer(self, buffer_sect);
    return RING_BUFFER_OK;
}

static buffer_err RingBuffer_Write(RingBuffer *const self, uint8_t *buf, buf_size_t buflen)
{
    buf_size_t free_len = self->total_buf_len - self->used_buf_len;
    if (free_len < buflen) {
        return RING_BUFFER_FAIL;
    }
    buf_size_t left_len = buflen; // to write len
    RingBufferSection *sect = self->head.sect;
    buf_size_t offset = self->head.offset;
    while (left_len > 0) {
        buf_size_t node_left_len = sect->buflen - offset;  // could not right
        buf_size_t write_len; // min(left_len, node_left_len)
        if (node_left_len >= left_len) {
            write_len = left_len;
        } else {
            write_len = node_left_len;
        }
        if (write_len)
            memcpy(&sect->buf[offset], &buf[buflen - left_len], write_len);
        left_len -= write_len;
        if (left_len) { // curr sect not enough
            sect = sect->next;
            offset = 0;
        } else {
            // update head pos
            self->head.sect = sect;
            self->head.offset = write_len;
        }
    }
    self->used_buf_len += buflen;
    return RING_BUFFER_OK;
}

static buffer_err RingBuffer_Read(RingBuffer *const self, uint8_t *buf, buf_size_t buflen)
{
    buf_size_t read_len = 0;
    if (buflen > self->used_buf_len) {
        read_len = self->used_buf_len;
    } else {
        read_len = buflen;
    }

    RingBufferSection *sect = self->tail.sect;
    buf_size_t node_offset = 0;
    buf_size_t read_offset = 0;
    buf_size_t node_left_len = 0;

    buf_size_t read_left = read_len;
    while (read_left > 0) {
        node_offset = self->tail.offset;
        node_left_len = sect->buflen - node_offset;
        if (node_left_len >= read_left) {
            memcpy(buf + read_offset, &sect->buf[node_offset], read_left);
            self->tail.offset += read_left;
            read_left -= read_left;
            read_offset += read_left;
        } else {
            memcpy(buf + read_offset, &sect->buf[node_offset], node_left_len);
            read_offset += node_left_len;
            node_offset += node_left_len;
            read_left -= node_left_len;
            self->tail.sect = self->tail.sect->next;
            self->tail.offset = 0;
            sect = sect->next;
        }
    }
    self->used_buf_len -= read_len;
    return read_len;
}

static buf_size_t RingBuffer_GetFreeCount(RingBuffer *const self)
{
    return self->total_buf_len - self->used_buf_len;
}

buffer_err RingBuffer_Ctor(RingBuffer *const buffer)
{
    assert(buffer != NULL);
    buffer->extend_dynamic_buffer = RingBuffer_ExtendDynamicBuffer;
    buffer->extent_static_buffer = RingBuffer_ExtendStaticBuffer;
    buffer->get_total_count = RingBuffer_GetTotalCount;
    buffer->get_free_count = RingBuffer_GetFreeCount;
    buffer->write = RingBuffer_Write;
    buffer->read = RingBuffer_Read;
    buffer->total_buf_len = 0;
    buffer->used_buf_len = 0;
    buffer->buffer_list = NULL;
    buffer->head.sect = NULL;
    buffer->head.offset = 0;
    buffer->tail.sect = NULL;
    buffer->tail.offset = 0;
    return RING_BUFFER_OK;
}

